package com.example.infrastructure.domain.organization.service;

import com.example.infrastructure.api.organization.vo.Condition;
import com.example.infrastructure.domain.organization.aggregate.Organization;
import com.example.infrastructure.domain.organization.repo.OrganizationRepo;
import com.example.infrastructure.domain.organization.repo.po.OrganizationPo;
import org.springframework.data.domain.Example;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.EntityNotFoundException;
import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * TODO
 *
 * @author lxh
 * @date 2020/11/17 下午1:30
 **/
@Service
public class OrganizationDomainService {
    private final OrganizationRepo organizationRepo;

    public OrganizationDomainService(OrganizationRepo organizationRepo) {
        this.organizationRepo = organizationRepo;
    }

    public Optional<Organization> findByOrgId(String orgId) throws EntityNotFoundException {
        return organizationRepo.findOne(Example.of(Organization.createByOrgId(orgId)));
    }

    public List<Organization> findOrganizations(Organization request) {
        return organizationRepo.findAllByExample(Example.of(request));
    }

    public Organization save(Organization organization) {
        return organizationRepo.save(organization);
    }

    public void delete(Organization organization) {
        organizationRepo.delete(organization);
    }

    public void deleteByOrgId(String orgId) {
        organizationRepo.deleteById(orgId);
    }

    public List<Organization> search(Condition condition) {
        return organizationRepo.findAllBySpecification(getSpecification(condition));
    }

    private Specification<OrganizationPo> getSpecification(Condition condition) {
        return (root, query, criteriaBuilder) -> toPredicate(root, query, criteriaBuilder, condition);
    }

    private Predicate toPredicate(
            Root<OrganizationPo> root, CriteriaQuery<?> query, CriteriaBuilder cb, Condition condition) {

        String fieldName = condition.getFieldName();
        String operator = condition.getOperator();
        String value = condition.getValue();
        Condition preCondition = condition.getPreCondition();
        Condition postCondition = condition.getPostCondition();

        List<Predicate> predicates = new ArrayList<>();

        // 分组查询
        if (preCondition != null && postCondition != null) {
            switch (operator) {
                case "and":
                    predicates.add(cb.and(
                            toPredicate(root, query, cb, preCondition), toPredicate(root, query, cb, postCondition)));
                    break;
                case "or":
                    predicates.add(cb.or(
                            toPredicate(root, query, cb, preCondition), toPredicate(root, query, cb, postCondition)));
                    break;
                default:
            }
        } else {
            // 基本查询
            if (!StringUtils.hasText(fieldName)) {
                throw new IllegalArgumentException("字段名不能为空！");
            }
            if (!StringUtils.hasText(value)) {
                throw new IllegalArgumentException("条件值不能为空！");
            }
            Path<String> path = root.get(fieldName);

            switch (operator) {
                case "=":
                    predicates.add(cb.equal(path, value));
                    break;
                case "<>":
                    predicates.add(cb.notEqual(path, value));
                    break;
                case ">":
                    predicates.add(cb.greaterThan(path, value));
                    break;
                case ">=":
                    predicates.add(cb.greaterThanOrEqualTo(path, value));
                    break;
                case "<":
                    predicates.add(cb.lessThan(path, value));
                    break;
                case "<=":
                    predicates.add(cb.lessThanOrEqualTo(path, value));
                    break;
                case "like":
                    predicates.add(cb.like(path, "%" + value + "%"));
                    break;
                case "between":
                    String[] values = value.split("\\.");
                    predicates.add(cb.between(path, values[0], values[1]));
                    break;
                default:
            }
        }
        return cb.and(predicates.toArray(new Predicate[0]));
    }
}
